grammar XYZ;

options {
    //language=CSharp3;
    output=AST;
    ASTLabelType=CommonTree;
	backtrack = true;
}


tokens{
	ARRAYINIT;
	STATEMENT;
	MAINMETHOD;
    CLASSBODYDECL;
	CLASSDECL;
	CLASSMEMBERDECL;
	METHODDECL;
	FIELDDECL;
	VARDECL;
	EXPR;
	PARAMS;
	BLOCK;
	CALL;
	VARACCESS;
	MEMBERACCESS;
	ARRAYACCESS;
	PREDECL;
	POSTDECL;
	ARGUS;
	TYPE;
	ARRTYPE;
	PARAMDECL;
	ARRAY;
	STATEMENT;
	EXPRSTART;
	ASSIGNSTAT;
	LONGARRAY;
	INTARRAY;
	BOOLTYPE;
	LITERALTYPE;

}
@lexer::namespace { XYZCompiler }
@parser::namespace { XYZCompiler }

@parser::members{
public int array_member_assign = 0;
public int var_assign = 0;
public int return_num =0;
public int array_init = 0;
public int var_init =0;
public delegate void ErrorHandler(string s, int column, int row);
public event ErrorHandler Error;
public override void ReportError(Antlr.Runtime.RecognitionException e)
{
	string s;
    if (e.Token != null)
    {
        string TokenType = "";
        if (e.Token.Type > 0)
            TokenType = TokenNames[e.Token.Type];
        s = string.Format("Token:{0} TokenType:{1}", e.Token.Text, TokenType);
    }
    else
        s = "";
    if(e is MismatchedSetException||e is MismatchedTokenException || e is MismatchedTreeNodeException)
    {
        int expecting = (int)(e.GetType().GetProperty("Expecting").GetValue(e,null));
        s += "    Expecting:"+this.TokenNames[expecting];
    }
	if(Error!=null)
		Error(s,e.CharPositionInLine,e.Line);
}
}
@lexer::members{
public override void Emit(IToken t)
{
	if(t!=null)
	{
		if(t.Type != 30)
		{
			if(!key_stat.ContainsKey(t.Text))
				key_stat.Add(t.Text,0);
			key_stat[t.Text]++;
		}
		else
		{
			if(!id_stat.ContainsKey(t.Text))
				id_stat.Add(t.Text,0);
			id_stat[t.Text]++;
		}
	}
	base.Emit(t);
}
public Dictionary<string,int> key_stat = new Dictionary<string,int>();
public Dictionary<string,int> id_stat = new Dictionary<string,int>();
public delegate void ErrorHandler(string s, int column, int row);
public event ErrorHandler Error;
public override void ReportError(Antlr.Runtime.RecognitionException e)
{
    var ex = e as MismatchedTokenException;
	if(ex == null)
		throw new System.Exception("Opps!");
	if(Error!=null)
		Error("MismatchedToken",ex.CharPositionInLine,ex.Line);
}
}
public
program	returns [int a]
	: mainClassDeclaration  (classDeclaration)*
	{
		$a=123;
	}
	;

classDeclaration
	:('class' ID
	('extends' type
	)?
	'{'
	(variableDeclaration )*
	methodDeclaration*
	'}'
	)->^(CLASSDECL ID ^('extends' type)? variableDeclaration* methodDeclaration*)
	;
mainClassDeclaration
	:('class' ID
	'{'
	variableDeclaration*
	methodDeclaration*
	'static' 'void' 'main' '(' ')'
	'{' statement* '}'
	'}'
	)->^(CLASSDECL ID variableDeclaration* methodDeclaration* ^(MAINMETHOD 'void' 'main' PARAMS ^(BLOCK statement*)))
	;

classBodyDeclaration
	:'{'
	(variableDeclaration )*
	methodDeclaration*
	'}'
	;

mainMethodDeclaration
	:('static' 'void' 'main' '(' ')'
	'{' statement* '}'
	'}'
	)->^(MAINMETHOD ^(BLOCK statement*))
	;

methodDeclaration
	:
	(
	type
	ID
	formalParameteres
	methodBlock
	)->^(METHODDECL type ID formalParameteres methodBlock)
	;
methodBlock
	:('{'
	(preDecl)
	(postDecl)
	(statement)*
	'}'
	)->^(BLOCK preDecl postDecl (statement)*)
	;
preDecl
	:('pre:' expression ';'
	)->^(PREDECL expression)
	;
postDecl
	:('post:' expression ';'
	)->^(POSTDECL expression)
	;
block	:('{'
	(statement
	)*
	'}')->^(BLOCK statement*)
	;
statement 
    :   (block)->^(STATEMENT block)          
    |   ('if'  '(' expression ')'  statement 'else' statement)->^(STATEMENT  ^('if' expression statement statement ))   
    |   'while'^ '('! expression ')'! statement
    |   ('return' expression  ';'){return_num++;}->^(STATEMENT ^('return' expression))
    |   (ID '<=' expression ';'){var_assign++;}->^(STATEMENT ^('<=' ID expression))
    |	(ID '[' expression ']' '<=' expression ';'){array_member_assign++;}->^(STATEMENT ^('<=' ID expression expression))
    |	(variableDeclaration) -> ^(STATEMENT variableDeclaration)	
    ;

parExpression
	:'('! expression')'!
	;

formalParameteres
	:('('
	(formalParametersDecl)
	')'
	)->^(PARAMS formalParametersDecl)
	;

formalParametersDecl
	:parameterDecl
	(','! parameterDecl)*
	;

parameterDecl
	:(type ID)->^(PARAMDECL type ID)
	;


variableInitializer
	:('[' expressionList ']')->^(ARRAYINIT expressionList)
	|expression
	;
variableDeclaration
	:
	(type ID ('<=' variableInitializer)?';')->^(VARDECL type ID ^('<=' variableInitializer)?)
	;
	
type	:classType->^(classType)
	|primitiveType->^(primitiveType)
	;

classType
	:ID
	;

primitiveType
    :   'boolean'
    |   'int'
    |   'long'
    |   ('int' '[]')->INTARRAY
    | 	('long' '[]')->LONGARRAY
    ;


//expression 
//    :   conditionalOrExpression
//        (assignmentOperator^ expression
//        )?
//    ;
expressionList 
    :   expression
        (','! expression
        )*
    ;
arguments 
    :   '('! (expressionList
        )? ')'!
    ;

//expression
    //:   expressionStart->^(EXPRSTART expressionStart)
    //;
//expression
//	:	orexpression '.' 'length'
//	|   orexpression '.' ID
//	;
expression
	:   (conditionalAndExpression
        ('||'^ conditionalAndExpression)*)
        ;
conditionalAndExpression 
    :   equalityExpression 
        ('&&'^ equalityExpression 
        )*
    ;


equalityExpression 
    :   relationalExpression
        (   
            (   '=='
            |   '!='
            )^
            relationalExpression
        )*
    ;


relationalExpression 
    :   additiveExpression
        (relationalOp^ additiveExpression
        )*
    ;

relationalOp 
    :    '<' '='
    |    '>' '='
    |   '<'
    |   '>'
    ;




additiveExpression 
    :   multiplicativeExpression
        (   
            (   '+'
            |   '-'
            )^
            multiplicativeExpression
         )*
    ;

multiplicativeExpression 
    :
        (callExpression
        (   
            (   '*'
            |   '/'
            )^
            callExpression
        )*)
    ;

/**
 * NOTE: for '+' and '-', if the next token is int or long interal, then it's not a unary expression.
 *       it's a literal with signed value. INTLTERAL AND LONG LITERAL are added here for this.
 */
callExpression
	:(arrayExpression ('.'! 'length'^)? )
	|(arrayExpression ('.'! ID '('^ expressionList? ')'!)?)
	;
arrayExpression
	:(accessExpression ('['^ expression']'!)?)
	;
accessExpression
	//:memberAccessExpression
	//(selector)*
	:   ID->^(VARACCESS ID)
	|   'this'->^(VARACCESS 'this')
	|   IntergerNumber->^(LITERALTYPE IntergerNumber)
    |   TRUE->^(BOOLTYPE TRUE)
    |   FALSE->^(BOOLTYPE FALSE)
	|   primary
	;


primary 
    :   parExpression            
    |   creator
    ;

creator 
    :   'new'^ ID '('! ')'!
    |   'new'^ 'int' '('!expression')'!
	|   'new'^ 'long' '('!expression')'! 
    ;

COMMENT
    :   '//' ~('\n'|'\r')* '\r'? '\n' {Skip();}
    |   '/*' ( options {greedy=false;} : . )* '*/'{Skip();}
    ;

WS  :   ( ' '
        | '\t'
        | '\r'
        | '\n'
        ) {Skip();}
    ;

LENGTH
	:'length'
	;
CLASS	
	:'class'
	;
EXTENDS
	:'extends'
	;
STATIC
	:'static'
	;
RETURN
	:'return'
	;
IF
	:'if'
	;
WHILE
	:'while'
	;
TRUE
	:'true'
	;
FALSE
	:'false'
	;
NEW
	:'new'
	;
PRE
	:'pre:'
	;
POST
	:'post:'
	;
LBRACE
	:'('
	;
RBRACE
	:')'
	;
LPAREN
	:'{'
	;
RPAREN
	:'}'
	;

INT
	:'int'
	;

LONG
	:'long'
	;
BOOLEAN
	:'boolean'
	;
VOID
	:'void'
	;
THIS
	:'this'
	;

COMMA
	:','
	;
EQUALEQUAL
	:'=='
	;
ASSIGN
	:'<='
	;
DOT
	:'.'
	;
SEMI
	:';'
	;
ANDAND
	:'&&'
	;
OROR
	:'||'
	;
EQUAL
	:'='
	;
GT
	:'>'
	;
LT
	:'<'
	;
ADD
	:'+'
	;
SUB
	:'-'
	;
STAR
	:'*'
	;
SLASH
	:'/'
	;
ERRORID:
	('0'..'9')('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
	;
IntergerNumber
:	'0'..'9'+
;

ID :	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*   // {if(!id_stat.ContainsKey(idt.text))id_stat.Add(idt.text,0);id_stat[idt.text]++;} 
	;

